/*
 * Ported.java
 *
 * Created on August 1, 2006, 10:15 AM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package com.smallx.xproc;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.infoset.xml.Name;
import org.infoset.xml.NamespaceScope;

/**
 *
 * @author alex
 */
public class StepContainer extends Step {

   StepContainer parent;
   Map<Name,Step> steps;
   Map<Name,List<PortReference>> inputReferences;
   Map<Name,PortReference> outputs;
   
   /** Creates a new instance of Ported */
   protected StepContainer(Name name,StepContainer parent) {
      super(new ComponentType(name),parent,name,null);
      this.steps = new HashMap<Name,Step>();
      this.parameters = null;
      this.parent = parent;
      this.inputReferences = new HashMap<Name,List<PortReference>>();
      this.outputs = new HashMap<Name,PortReference>();
   }
   
   public StepContainer getContainer() {
      return parent;
   }
   
   public Collection<PortReference> getInputReferences(Name name) {
      return inputReferences.get(name);
   }
   
   public PortReference getOutputSource(Name name) {
      return outputs.get(name);
   }
   
   public void defineInputPort(PortDefinition def) 
      throws ModelErrorException
   {
      if (type.getInputPort(def.getName())!=null) {
         throw new ModelErrorException("Port with name "+def.getName()+" already exists on "+this.getName(),this);
      }
      type.addInputPort(def.getName(),def.isSequenceAllowed());
      Reference ref = def.getReference();
      if (ref!=null) {
         bindInput(def.getName(),ref);
      }
      inputReferences.put(def.getName(),new ArrayList<PortReference>());
   }
   
   public void defineOutputPort(PortDefinition def) 
      throws ModelErrorException
   {
      if (type.getOutputPort(def.getName())!=null) {
         throw new ModelErrorException("Port with name "+def.getName()+" already exists on "+this.getName(),this);
      }
      type.addOutputPort(def.getName(),def.isSequenceAllowed());
      Reference ref = def.getReference();
      if (ref==null) {
         throw new ModelErrorException("Step container output port "+def.getName()+" must have a reference to a step output port.");
      }
      if (ref instanceof PortReference) {
         outputs.put(def.getName(),(PortReference)ref);
      } else {
         throw new ModelErrorException("Output port declarations may only contain port references.");
      }
   }
   
   public Step createStep(ComponentType type,Name sname, NamespaceScope scope) 
      throws ModelErrorException
   {
      Step step = new Step(type,this,sname,scope);
      addStep(step);
      return step;
   }
   
   public void addStep(Step step) 
      throws ModelErrorException
   {
      if (steps.get(step.getName())!=null) {
         throw new ModelErrorException("Step container "+itemName+" already contains "+step.getName());
      }
      if (step!=this && step instanceof StepContainer) {
         StepContainer container = (StepContainer)step;
         for (Name name : container.getStepNames()) {
            if (steps.get(name)!=null) {
               throw new ModelErrorException("Step container "+itemName+" already contains "+name);
            }
            steps.put(name,container.getStep(name));
         }
      }
      steps.put(step.getName(),step);
   }
   
   public void bindOutput(Name name,PortReference ref) 
      throws ModelErrorException
   {
      if (ref instanceof PortReference) {
         PortReference pref = (PortReference)ref;
         //System.err.println("Container "+itemName+" step "+pref.getStepName());
         Step containedStep  = steps.get(pref.getStepName());
         //System.err.print("Contained steps ");
         //for (Name key : steps.keySet()) {
         //   System.err.print(" "+key);
         //}
         //System.err.println();
         //System.err.println(itemName+"!"+name+"-> "+pref.getStepName()+"!"+pref.getPortName()+", Contained step="+containedStep);
         if (containedStep!=null) {
            List<PortReference> list = inputReferences.get(name);
            if (list==null) {
               throw new ModelErrorException("There is no input named "+name+" on step container "+itemName);
            }
            list.add(pref);
            return;
         }
      }
      super.bindOutput(name,ref);
   }
   
   public Set<Name> getStepNames() {
      return steps.keySet();
   }
   
   public Step getStep(Name sname) {
      return steps.get(sname);
   }
   
}
